#include <os/walltime.h>
#include <os/memcache.h>
#include <os/path.h>
#include <os/kernelif.h>
#include <os/safety.h>
#include <os/log.h>
#include <os/safety.h>
#include <os/debug.h>
#include <lib/unistd.h>
#include <sys/fcntl.h>
#include <lib/type.h>
#include <lib/string.h>
#include <lib/stddef.h>
#include <lib/list.h>
#include <lib/errno.h>

LIST_HEAD(log_list_head);

void LogInit()
{
    int dir_no_exist = 0;
    char path[MAX_PATH_LEN + 1];
    strcpy(path, LOG_DIR_PATH);

    int fd = KFileOpenDir(path); // open log dir
    if (fd < 0)
    {
        dir_no_exist = 1;
    }
    KFileCloseDir(fd);

    if (dir_no_exist) // new dir
    {
        if (KFileMkDir(path, O_CREATE | O_RDWR) < 0)
        {
            KPrint("[log] create log dir %s failed!\n", path);
            return;
        }
        KPrint("[log] create log dir ok!\n");

        // create system log file
        memset(path, 0, MAX_PATH_LEN);
        strcpy(path, LOG_SYSTEM_PATH);
        fd = KFileOpen(path, O_CREATE | O_WRONLY);
        if (fd < 0)
        {
            KPrint("[log] open system log %s file failed!\n", LOG_SYSTEM_PATH);
            return;
        }
        KFileClose(fd);

        // create app log file
        memset(path, 0, MAX_PATH_LEN);
        strcpy(path, LOG_APP_PATH);
        fd = KFileOpen(path, O_CREATE | O_RDONLY);
        if (fd < 0)
        {
            KPrint("[log] open app log %s file failed!\n", LOG_APP_PATH);
            return;
        }
        KFileClose(fd);
        KPrint("[log] log file create ok!\n");

        // create log
        LogWrite("System log1", "system log create successful!", 0, LOG_TYPE_SYSTEM, LOG_EVNET_TYPE_NORMAL);
        LogWrite("App log1", "applicate log create successful!", 0, LOG_TYPE_APP, LOG_EVNET_TYPE_NORMAL);
    }

    // dump
    /*LogDump(LOG_TYPE_SYSTEM);
    LogDump(LOG_TYPE_APP);*/
}

int LogWrite(char *name, char *context, uint32_t error, uint8_t type, uint8_t event_type)
{
    char path[MAX_PATH_LEN + 1];
    int fd;
    log_t *log = KMemAlloc(sizeof(log_t));

    switch (type)
    {
    case LOG_TYPE_APP:
        // open log file
        strcpy(path, LOG_APP_PATH);
        fd = KFileOpen(path, O_APPEND | O_WRONLY); // open to append mode
        if (fd < 0)
        {
            KPrint("[log] open app log %s failed!\n", LOG_APP_PATH);
            return -1;
        }
        // make log
        log->year = walltime.year;
        log->month = walltime.month;
        log->day = walltime.day;
        log->hour = walltime.hour;
        log->minute = walltime.minute;
        log->second = walltime.second;
        log->event_type = event_type;
        log->error_code = error;
        strcpy(log->title, name);
        strcpy(log->context, context);
        log->type = type;
        list_init(&log->list);

        // write file
        if (KFileWrite(fd, log, sizeof(log_t)) != sizeof(log_t))
        {
            KPrint("[log] log type[%d] event_type[%d] title[%s] write %s failed!\n", type, event_type, name, path);
            KFileClose(fd);
            KMemFree(log);
            return -1;
        }
        KPrint("[log] write log type %d title %s\n", log->type, log->title);
        KFileClose(fd);
        KMemFree(log);
        break;
    case LOG_TYPE_SYSTEM:
        // open log file
        strcpy(path, LOG_SYSTEM_PATH);
        fd = KFileOpen(path, O_APPEND | O_WRONLY); // open to append mode
        if (fd < 0)
        {
            KPrint("[log] open system log %s failed!\n", LOG_SYSTEM_PATH);
            return -1;
        }
        // make log
        log->year = walltime.year;
        log->minute = walltime.minute;
        log->day = walltime.day;
        log->hour = walltime.hour;
        log->minute = walltime.minute;
        log->second = walltime.second;
        strcpy(log->title, name);
        strcpy(log->context, context);
        log->event_type = event_type;
        log->type = type;
        log->error_code = error;
        list_init(&log->list);

        if (KFileWrite(fd, log, sizeof(log_t)) < 0)
        {
            KPrint("[log] log type[%d] event_type[%d] title[%s] write %s failed!\n", type, event_type, name, path);
            KFileClose(fd);
            KMemFree(log);
            return -1;
        }
        KPrint("[log] write log type %d title %s\n", log->type, log->title);
        KFileClose(fd);
        KMemFree(log);
        break;
    default:
        KPrint("[log] no support log type %d now!\n", type);
        KMemFree(log);
        return -1;
        break;
    }
    return 0;
}

int LogFind(int title, int type, log_t *buff)
{
    char path[MAX_PATH_LEN + 1];

    switch (type)
    {
    case LOG_TYPE_SYSTEM:
        strcpy(path, LOG_SYSTEM_PATH);
        break;
    case LOG_TYPE_APP:
        strcpy(path, LOG_APP_PATH);
        break;
    }

    int fd = KFileOpen(path, O_RDONLY);
    if (fd < 0)
    {
        KPrint("[log] open log file %s failed!\n", path);
        return -1;
    }

    // get file size
    status_t fstu;
    if (KFileStatus(path, &fstu) < 0)
    {
        KPrint("[log] get file %s status failed!\n", path);
        return -1;
    }

    int size = fstu.st_size;
    if (size < 0)
    {
        KPrint("[log] file %s size invalid!\n", path);
        return -1;
    }
    char *t_buff = (char *)KMemAlloc(size);
    if (!t_buff)
    {
        KPrint("[log] mem alloc for buff failed!\n");
        return -1;
    }
    int reads = KFileRead(fd, t_buff, size);
    if (reads <= 0)
    {
        KPrint("[log] read file %s error!\n", path);
        KFileClose(fd);
        return -1;
    }
    KFileClose(fd);

    log_t *log = t_buff;
    while (log && log < t_buff + size)
    {
        // copy to new buff and return
        if (!strcmp(log->title, title))
        {
            if (!buff)
            {
                KMemFree(t_buff);
                return 0;
            }
            memcpy(buff, log, sizeof(log_t));
            KMemFree(t_buff);
            return 0;
        }
        log++; // next log
    }
    KMemFree(t_buff);
    return -1;
}

int LogDel(int title, int type)
{
    char path[MAX_PATH_LEN + 1];

    switch (type)
    {
    case LOG_TYPE_SYSTEM:
        strcpy(path, LOG_SYSTEM_PATH);
        break;
    case LOG_TYPE_APP:
        strcpy(path, LOG_APP_PATH);
        break;
    }

    int fd = KFileOpen(path, O_RDONLY);
    if (fd < 0)
    {
        KPrint("[log] open log file %s failed!\n", path);
        return -1;
    }

    // get file size
    status_t fstu;
    if (KFileStatus(path, &fstu) < 0)
    {
        KPrint("[log] get file %s status failed!\n", path);
        return -1;
    }
    int size = fstu.st_size;
    if (size <= 0)
    {
        KPrint("[log] file %s size invalid!\n", path);
        return -1;
    }
    char *buff = KMemAlloc(size);
    if (!buff)
    {
        KPrint("[log] mem alloc for buff failed!\n");
        return -1;
    }
    int reads = KFileRead(fd, buff, size);
    if (reads < 0)
    {
        KPrint("[log] read file %s error!\n", path);
        KMemFree(buff);
        KFileClose(fd);
        return -1;
    }
    KFileClose(fd);

    // build to list from buffer
    log_t *log = buff;
    while (log && (uint32_t)log < (uint32_t)buff + size)
    {
        // add to list
        list_add_tail(&log->list, &log_list_head);
        log++; // next log
    }

    // del list object for list
    log = NULL;
    list_traversal_all_owner_to_next(log, &log_list_head, list)
    {
        if (!strcmp(log->title, title))
        {
            list_del(&log->list);
            break;
        }
    }

    // sync data
    fd = KFileOpen(path, O_WRONLY | O_TRUNC);
    if (fd < 0)
    {
        KPrint("[log] open file %s failed!\n", path);
        return -1;
    }
    list_traversal_all_owner_to_next(log, &log_list_head, list)
    {
        if (KFileWrite(fd, log, sizeof(log_t)) < 0)
        {
            KPrint("[log] sync data error!\n");
            KMemFree(buff);
            KFileClose(fd);
            return -1;
        }
    }
    KMemFree(buff);
    KFileClose(fd);
    return 0;
}

// reads a log data and return 0
// return -1 if file point had arrive to eof or happen errors
int LogRead(int type, log_t *buff)
{
    static uint64_t last_off = 0;

    char path[MAX_PATH_LEN + 1];

    switch (type)
    {
    case LOG_TYPE_SYSTEM:
        strcpy(path, LOG_SYSTEM_PATH);
        break;
    case LOG_TYPE_APP:
        strcpy(path, LOG_APP_PATH);
        break;
    }

    int fd = KFileOpen(path, O_RDONLY);
    if (fd < 0)
    {
        KPrint("[log] open log file %s failed!\n", path);
        return -1;
    }

    // start of last offset
    KFileLSeek(fd, last_off * sizeof(log_t), SEEK_SET);
    int reads = KFileRead(fd, buff, sizeof(log_t) - sizeof(list_t)); // no read list data area
    if (reads < 0)
    {
        KPrint("[log] read file %s error!\n", path);
        return -1;
    }
    if (reads == 0)
    {
        return -1;
    }
    last_off++; // record to next position
    KFileClose(fd);
    return 0;
}

void LogDump(int type)
{
    char path[MAX_PATH_LEN + 1];

    switch (type)
    {
    case LOG_TYPE_SYSTEM:
        strcpy(path, LOG_SYSTEM_PATH);
        break;
    case LOG_TYPE_APP:
        strcpy(path, LOG_APP_PATH);
        break;
    default:
        return;
    }

    int fd = KFileOpen(path, O_RDONLY);
    if (fd < 0)
    {
        KPrint("[log] log file %s open failed!\n", LOG_SYSTEM_PATH);
        return;
    }

    status_t fstu;
    KFileStatus(path, &fstu);
    int size = fstu.st_size;
    if (size <= 0)
        return;

    char *buff = KMemAlloc(size);
    if (KFileRead(fd, buff, size) < 0)
    {
        KPrint("[log] read log file %s failed!\n", path);
        return;
    }
    KFileClose(fd);

    log_t *log = buff;
    while (log->title[0] && log->context[0] && log <= buff + size)
    {
        KPrint("[log] type: %d event_type: %d date: %d.%d.%d %d:%d:%d title: %s context: %s\n", log->type, log->event_type, log->year, log->month, log->day, log->hour, log->minute, log->second, log->title, log->context);
        log++;
    }
    KMemFree(buff);
}

int SysLogWrite(int type, char *title, char *context, int event_type, int err)
{
    return LogWrite(title, context, err, type, event_type);
}

int SysLogRead(int type, log_t *buff)
{
    if (SafetyCheckRange(buff, sizeof(log_t)) < 0)
        return -EINVAL;
    return LogRead(type, buff);
}

int SysLogDel(int type, char *title)
{
    if (SafetyCheckRange(title, strlen(title)) < 0)
        return -EINVAL;
    return LogDel(title, type);
}

int SysLogFind(int type, char *title,char *buff)
{
    if(SafetyCheckRange(title,strlen(title))<0)
        return -EINVAL;
    return LogFind(title,type,buff);
}